/**
 * MooGraph, created by Korri(.fr)
 *
 * See http://www.korri.fr/ for more informations.
 *
 * @version 0.8
 * @author Korri (http://www.korri.fr/)
 */
var MooGraph = new Class({
    Implements: Options,
    /* Properties */
    options: {
        width:  400,
        height: 200,
        margin: 20,
        space: 2,
        defaultBullet: 'disc',
        defaultSize: 4,
        growing: 1.5,
        colors:{
            background: '#FFF',
            bulletsbg: '#FFF',
            text: '#000',
            lines: new Array('#07C','#5A5','#C44','#F60','#94F','#EC4','#BDF'),
            tooltipBorder: '#474747',
            tooltipBackground: '#FFF'
        },
        formatTime: function(cnt){
            return cnt;
        },
        formatValue: function(val){
            return val;
        },
        onClick: function(pt){
            return;
        },
        tooltipRadius: 2,
        minx: null,
        miny: null,
        maxx: null,
        maxy: null
    },
    clr: 0,
    container: null,
    canvas: null,
    charts: new Array(),

    maxx: -1.7976931348623157E+10308,
    maxy: -1.7976931348623157E+10308,
    minx:  1.7976931348623157E+10308,
    miny:  1.7976931348623157E+10308,
    xunit: 0,
    yunit: 0,

    big: null,//Actually growed bullet
    
    /* Functions */
    initialize: function(id, options){
        this.setOptions(options);

        this.maxx = this.options.maxx!=null? this.options.maxx : -1.7976931348623157E+10308;
        this.maxy = this.options.maxy!=null? this.options.maxy : -1.7976931348623157E+10308;
        this.minx = this.options.minx!=null? this.options.minx : 1.7976931348623157E+10308;
        this.miny = this.options.miny!=null? this.options.miny : 1.7976931348623157E+10308;

        var target = $(id);
        if(!target) this.error('Invalid id, MooGraph has to be called giving the id of a valid element !');

        this.container = new Element('div', {
            width: Number(this.options.width),
            height: Number(this.options.height),
            style: 'position:relative; cursor:default'
        });
        target.grab(this.container);

        this.canvas = new Element('canvas', {
            width: Number(this.options.width),
            height: Number(this.options.height)
        });
        if (!this.canvas.getContext) this.err('Browser doesn\'t suports canvas.');
        this.canvas = this.canvas;
        this.ctx = this.canvas.getContext('2d');

        if(this.options.buildNow) this.build();
    },
    click: function(ev){
        if(this.big){
            this.options.onClick(this.big);
        }
    },
    move: function(ev){
        var pos = this.canvas.getPosition();
        var x = ev.page.x - pos.x;
        var y = ev.page.y - pos.y;

        if(this.big && this.distance(this.big, {x:x,y:y}) >= (this.big.size*this.options.growing)+3){
            this.big = null;
            this.container.style.cursor = 'default';
            this.reDraw();
        }

        if(!this.big){
            for(var i = this.charts.length - 1; i >= 0; i--){
                
                var size = this.charts[i].vals.length;

                for(var k = 0; k<size; k++){
                    var pt = this.charts[i].vals[k];

                    if(this.distance(pt, {x:x,y:y}) < pt.size + 3){
                        this.big = pt;
                        break;
                    }
                }
                if(this.big) break;
            }

            if(this.big){
                this.drawBullet(this.big,true);
                this.container.style.cursor = 'pointer';
            }

        }

        //this.ctx.fillStyle = '#000';
        //this.ctx.fillRect(x, y,1,1);
    },
    distance: function(pt1, pt2){
        var x = Math.abs(pt1.x - pt2.x);
        var y = Math.abs(pt1.y - pt2.y);
        return Math.sqrt(x*x + y*y);
    },
    reDraw: function(){
        this.ctx.fillStyle = this.options.colors.background;
        this.ctx.fillRect(0,0,this.options.width,this.options.height);
        this.container.getElements('.infoText').destroy();
        this.draw();
    },
    draw: function(){

        //Drawing axes ! TODO faire les calculs dans preparePoints
        this.ctx.fillStyle = "#CCC";

        if(this.miny <= 0 && this.maxy >= 0){
            var y = Math.round(this.options.height - this.options.margin + this.miny * this.yunit);
            this.ctx.fillRect(0, y-0.5, this.options.width, 1);
            this.ctx.save();
            this.ctx.fillStyle = "#000";
            this.addText('0', 0, y + 4,null,'left');
            this.ctx.restore();
        }
        var start = Math.ceil(this.minx / this.options.space);
        var end = Math.floor(this.maxx / this.options.space);
        for(var i = start; i <= end; i ++){
            var x = Math.round(this.options.margin + (i*this.options.space-this.minx) * this.xunit);
            this.ctx.fillRect(x-0.5, 0,1,this.options.height);

            this.ctx.save();
            this.ctx.fillStyle = "#000";
            this.addText(this.options.formatTime(i*this.options.space), x+3, this.options.height-12, i==end?this.options.margin - 3:null);
            this.ctx.restore();
        }

        //Drawing lines !
        this.ctx.lineWidth = 3;
        this.ctx.lineCap = 'round';
        this.ctx.lineJoin = 'bevel';

        this.charts.each(function(chart){

            var i = 0;
            this.ctx.strokeStyle = chart.color;
            this.ctx.beginPath();

            chart.vals.each(function(pt){

                if(i++) this.ctx.lineTo(pt.x, pt.y);
                else    this.ctx.moveTo(pt.x, pt.y);

            },this);
            this.ctx.stroke();

            //Drawing points !
            chart.vals.each(function(pt){

                this.drawBullet(pt);

            },this);


        }, this);
    },
    preparePoints: function(){

        this.xunit = (this.options.width-this.options.margin*2) / (this.maxx - this.minx);
        this.yunit = (this.options.height-this.options.margin*2) / (this.maxy - this.miny);

        this.charts.each(function(chart){
            chart.vals.each(function(pt){
                pt.x = Math.round(this.options.margin + (pt.xval-this.minx) * this.xunit);
                pt.y = Math.round(this.options.height - this.options.margin - (pt.yval-this.miny) * this.yunit);
            },this);
        }, this);
    },
    addGraph: function(xVals, yVals, opts){
        opts = opts || {};
        var color = opts.color || this.options.colors.lines[this.clr++];
        var bulletStyle = opts.bullet || this.options.defaultBullet;
        var bulletSize = opts.size || this.options.defaultSize;

        if(this.clr >= this.options.colors.lines.length) this.clr = 0;

        this.maxx = Math.max(Math.max.apply( Math, xVals),this.maxx);
        this.maxy = Math.max(Math.max.apply( Math, yVals),this.maxy);
        this.minx = Math.min(Math.min.apply( Math, xVals),this.minx);
        this.miny = Math.min(Math.min.apply( Math, yVals),this.miny);

        var chart = new Array();
        xVals.each(function(xval, key){
            var yval = yVals[key];

            chart.push({
                xval: xval,
                yval: yval,
                color: color,
                style: bulletStyle,
                size: bulletSize
            });

        });
        this.charts.push({
            color: color,
            bullet: bulletStyle,
            size: bulletSize,
            vals: chart
        });
    },
    build: function(){
        this.container.addEvent('mousemove', this.move.bindWithEvent(this));
        this.container.addEvent('click', this.click.bindWithEvent(this));

        this.preparePoints();

        this.draw();

        this.container.grab(this.canvas);
    },
    drawBullet: function (pt, big){

        var style   = pt.style || this.options.defaultBullet;
        var size    = pt.size || this.options.defaultSize;

        size = big?size*this.options.growing:size;

        this.ctx.strokeStyle = this.options.colors.bulletsbg;
        this.ctx.lineWidth = 3;
        this.ctx.fillStyle = pt.color;

        //Drawing the bullet
        this.ctx.beginPath();
        switch(style){
            case 'disc':
                this.ctx.arc(pt.x, pt.y, size,0,Math.PI*2,true);
                break;
            case 'square':
                this.ctx.moveTo(pt.x, pt.y-size);
                this.ctx.lineTo(pt.x+size, pt.y);
                this.ctx.lineTo(pt.x, pt.y+size);
                this.ctx.lineTo(pt.x-size, pt.y);
                this.ctx.lineTo(pt.x, pt.y-size);
                break;
            case 'diamond':
                this.ctx.moveTo(pt.x, pt.y-size*1.5);
                this.ctx.lineTo(pt.x+size, pt.y);
                this.ctx.lineTo(pt.x, pt.y+size*1.5);
                this.ctx.lineTo(pt.x-size, pt.y);
                this.ctx.lineTo(pt.x, pt.y-size*1.5);
                break;
            case 'none':
                break;
            default:
                this.error('Unknow bullet style '+style);
        }
        this.ctx.stroke();
        this.ctx.fill();

        //Drawing tooltip
        if(big){
            var info = String(this.options.formatValue(pt.yval));

            var width = 10 + info.length * 7;
            var height = 14;
            var x = pt.x + pt.size*this.options.growing+4;
            if(x+width > this.options.width) x = pt.x - (pt.size*this.options.growing + 4) - width;

            var y = pt.y - height;
            if(y < 0) y= 1;

            var radius = this.options.tooltipRadius;
            this.ctx.strokeStyle = this.options.colors.tooltipBorder;
            this.ctx.fillStyle = this.options.colors.tooltipBackground;

            this.roundRect(x,y,width,height,radius);

            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';

            this.ctx.fillStyle = '#000';
            this.addText(info ,x+radius/2, y+radius/2, width-radius);
        }
    },
    roundRect: function(x,y,width,height,radius){

        this.ctx.beginPath();
        this.ctx.moveTo(x+radius, y);
        this.ctx.lineTo(x+width - radius, y);
        this.ctx.arc(x+width-radius, y+radius, radius, Math.PI*1.5, 0, false);
        this.ctx.lineTo(x+width, y+radius);
        this.ctx.lineTo(x+width, y+height-radius);
        this.ctx.arc(x+width-radius, y+height-radius,radius,0, Math.PI*0.5, false);
        this.ctx.lineTo(x+width-radius, y+height);
        this.ctx.lineTo(x+radius, y+height);
        this.ctx.arc(x+radius, y+height-radius, radius, Math.PI*0.5, Math.PI,false);
        this.ctx.lineTo(x,y+height-radius);
        this.ctx.lineTo(x, y+radius);
        this.ctx.arc(x+radius, y+radius, radius, Math.PI, Math.PI*1.5, false);

        this.ctx.stroke();
        this.ctx.fill();
    },
    error: function(err){
        throw 'MooGraphException: '+err;
    },
    addText: function(text, x, y, width, align){
        var txt = new Element('div',{
            'text':  text,
            'style': ' position: absolute; color:'+this.options.colors.text+'; font-family:arial; top:'+y+'px; left:'+x+'px; text-align: '+(align || 'center')+'; '+(width?'width:'+width+'px;':'')+' font-size: 12px',
            'class': 'infoText'
        });
        this.container.grab(txt);;
        return txt;
    }
});